{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| default_exp svg"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SVG\n",
    "> Simple SVG FT elements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "from fastcore.utils import *\n",
    "from fastcore.meta import delegates\n",
    "from fastcore.xml import FT\n",
    "from fasthtml.common import *\n",
    "from fasthtml.components import *\n",
    "from fasthtml.xtend import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from nbdev.showdoc import show_doc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "_all_ = ['AltGlyph', 'AltGlyphDef', 'AltGlyphItem', 'Animate', 'AnimateColor', 'AnimateMotion', 'AnimateTransform', 'ClipPath', 'Color_profile', 'Cursor', 'Defs', 'Desc', 'FeBlend', 'FeColorMatrix', 'FeComponentTransfer', 'FeComposite', 'FeConvolveMatrix', 'FeDiffuseLighting', 'FeDisplacementMap', 'FeDistantLight', 'FeFlood', 'FeFuncA', 'FeFuncB', 'FeFuncG', 'FeFuncR', 'FeGaussianBlur', 'FeImage', 'FeMerge', 'FeMergeNode', 'FeMorphology', 'FeOffset', 'FePointLight', 'FeSpecularLighting', 'FeSpotLight', 'FeTile', 'FeTurbulence', 'Filter', 'Font', 'Font_face', 'Font_face_format', 'Font_face_name', 'Font_face_src', 'Font_face_uri', 'ForeignObject', 'G', 'Glyph', 'GlyphRef', 'Hkern', 'Image', 'LinearGradient', 'Marker', 'Mask', 'Metadata', 'Missing_glyph', 'Mpath', 'Pattern', 'RadialGradient', 'Set', 'Stop', 'Switch', 'Symbol', 'TextPath', 'Tref', 'Tspan', 'Use', 'View', 'Vkern', 'Template']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "g = globals()\n",
    "for o in _all_: g[o] = partial(ft_hx, o[0].lower() + o[1:])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can create SVGs directly from strings, for instance (as always, use `NotStr` or `Safe` to tell FastHTML to not escape the text):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"50\" height=\"50\"><circle cx=\"20\" cy=\"20\" r=\"15\" fill=\"red\"></circle></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "svg = '<svg width=\"50\" height=\"50\"><circle cx=\"20\" cy=\"20\" r=\"15\" fill=\"red\"></circle></svg>'\n",
    "show(Safe(svg))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also use libraries such as [fa6-icons](https://www.fastht.ml/docs/fa6-icons/).\n",
    "\n",
    "To create and modify SVGs using a Python API, use the FT elements in `fasthtml.svg`, discussed below.\n",
    "\n",
    "**Note**: `fasthtml.common` does NOT automatically export SVG elements. To get access to them, you need to import `fasthtml.svg` like so\n",
    "\n",
    "```python\n",
    "from fasthtml.svg import *\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def Svg(*args, viewBox=None, h=None, w=None, height=None, width=None, xmlns=\"http://www.w3.org/2000/svg\", **kwargs):\n",
    "    \"An SVG tag; xmlns is added automatically, and viewBox defaults to height and width if not provided\"\n",
    "    if h: height=h\n",
    "    if w: width=w\n",
    "    if not viewBox and height and width: viewBox=f'0 0 {width} {height}'\n",
    "    return ft_svg('svg', *args, xmlns=xmlns, viewBox=viewBox, height=height, width=width, **kwargs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To create your own SVGs, use `SVG`. It will automatically set the `viewBox` from height and width if not provided.\n",
    "\n",
    "All of our shapes will have some convenient kwargs added by using `ft_svg`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@delegates(ft_hx)\n",
    "def ft_svg(tag: str, *c, transform=None, opacity=None, clip=None, mask=None, filter=None,\n",
    "           vector_effect=None, pointer_events=None, **kwargs):\n",
    "    \"Create a standard `FT` element with some SVG-specific attrs\"\n",
    "    return ft_hx(tag, *c, transform=transform, opacity=opacity, clip=clip, mask=mask, filter=filter,\n",
    "           vector_effect=vector_effect, pointer_events=pointer_events, **kwargs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Basic shapes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll define a simple function to display SVG shapes in this notebook:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def demo(el, h=50, w=50): return show(Svg(h=h,w=w)(el))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@delegates(ft_svg)\n",
    "def Rect(width, height, x=0, y=0, fill=None, stroke=None, stroke_width=None, rx=None, ry=None, **kwargs):\n",
    "    \"A standard SVG `rect` element\"\n",
    "    return ft_svg('rect', width=width, height=height, x=x, y=y, fill=fill,\n",
    "                 stroke=stroke, stroke_width=stroke_width, rx=rx, ry=ry, **kwargs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All our shapes just create regular `FT` elements. The only extra functionality provided by most of them is to add additional defined kwargs to improve auto-complete in IDEs and notebooks, and re-order parameters so that positional args can also be used to save a bit of typing, e.g:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><rect width=\"30\" height=\"30\" fill=\"blue\" rx=\"8\" ry=\"8\"></rect></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Rect(30, 30, fill='blue', rx=8, ry=8))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@delegates(ft_svg)\n",
    "def Circle(r, cx=0, cy=0, fill=None, stroke=None, stroke_width=None, **kwargs):\n",
    "    \"A standard SVG `circle` element\"\n",
    "    return ft_svg('circle', r=r, cx=cx, cy=cy, fill=fill, stroke=stroke, stroke_width=stroke_width, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><circle r=\"20\" cx=\"25\" cy=\"25\" stroke=\"red\" stroke-width=\"3\"></circle></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Circle(20, 25, 25, stroke='red', stroke_width=3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@delegates(ft_svg)\n",
    "def Ellipse(rx, ry, cx=0, cy=0, fill=None, stroke=None, stroke_width=None, **kwargs):\n",
    "    \"A standard SVG `ellipse` element\"\n",
    "    return ft_svg('ellipse', rx=rx, ry=ry, cx=cx, cy=cy, fill=fill, stroke=stroke, stroke_width=stroke_width, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><ellipse rx=\"20\" ry=\"10\" cx=\"25\" cy=\"25\"></ellipse></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Ellipse(20, 10, 25, 25))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def transformd(translate=None, scale=None, rotate=None, skewX=None, skewY=None, matrix=None):\n",
    "    \"Create an SVG `transform` kwarg dict\"\n",
    "    funcs = []\n",
    "    if translate is not None: funcs.append(f\"translate{translate}\")\n",
    "    if scale is not None: funcs.append(f\"scale{scale}\")\n",
    "    if rotate is not None: funcs.append(f\"rotate({','.join(map(str,rotate))})\")\n",
    "    if skewX is not None: funcs.append(f\"skewX({skewX})\")\n",
    "    if skewY is not None: funcs.append(f\"skewY({skewY})\")\n",
    "    if matrix is not None: funcs.append(f\"matrix{matrix}\")\n",
    "    return dict(transform=' '.join(funcs)) if funcs else {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'transform': 'rotate(45,25,25)'}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rot = transformd(rotate=(45, 25, 25))\n",
    "rot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><ellipse transform=\"rotate(45,25,25)\" rx=\"20\" ry=\"10\" cx=\"25\" cy=\"25\"></ellipse></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Ellipse(20, 10, 25, 25, **rot))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@delegates(ft_svg)\n",
    "def Line(x1, y1, x2=0, y2=0, stroke='black', w=None, stroke_width=1, **kwargs):\n",
    "    \"A standard SVG `line` element\"\n",
    "    if w: stroke_width=w\n",
    "    return ft_svg('line', x1=x1, y1=y1, x2=x2, y2=y2, stroke=stroke, stroke_width=stroke_width, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><line x1=\"20\" y1=\"30\" stroke=\"black\" stroke-width=\"3\"></line></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Line(20, 30, w=3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@delegates(ft_svg)\n",
    "def Polyline(*args, points=None, fill=None, stroke=None, stroke_width=None, **kwargs):\n",
    "    \"A standard SVG `polyline` element\"\n",
    "    if points is None: points = ' '.join(f\"{x},{y}\" for x, y in args)\n",
    "    return ft_svg('polyline', points=points, fill=fill, stroke=stroke, stroke_width=stroke_width, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><polyline points=\"0,0 10,10 20,0 30,10 40,0\" fill=\"yellow\" stroke=\"blue\" stroke-width=\"2\"></polyline></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Polyline((0,0), (10,10), (20,0), (30,10), (40,0),\n",
    "              fill='yellow', stroke='blue', stroke_width=2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><polyline points=\"0,0 10,10 20,0 30,10 40,0\" fill=\"purple\" stroke-width=\"2\"></polyline></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Polyline(points='0,0 10,10 20,0 30,10 40,0', fill='purple', stroke_width=2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@delegates(ft_svg)\n",
    "def Polygon(*args, points=None, fill=None, stroke=None, stroke_width=None, **kwargs):\n",
    "    \"A standard SVG `polygon` element\"\n",
    "    if points is None: points = ' '.join(f\"{x},{y}\" for x, y in args)\n",
    "    return ft_svg('polygon', points=points, fill=fill, stroke=stroke, stroke_width=stroke_width, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><polygon points=\"25,5 43.3,15 43.3,35 25,45 6.7,35 6.7,15\" fill=\"lightblue\" stroke=\"navy\" stroke-width=\"2\"></polygon></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Polygon((25,5), (43.3,15), (43.3,35), (25,45), (6.7,35), (6.7,15), \n",
    "             fill='lightblue', stroke='navy', stroke_width=2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><polygon points=\"25,5 43.3,15 43.3,35 25,45 6.7,35 6.7,15\" fill=\"lightgreen\" stroke=\"darkgreen\" stroke-width=\"2\"></polygon></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Polygon(points='25,5 43.3,15 43.3,35 25,45 6.7,35 6.7,15',\n",
    "             fill='lightgreen', stroke='darkgreen', stroke_width=2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@delegates(ft_svg)\n",
    "def Text(*args, x=0, y=0, font_family=None, font_size=None, fill=None, text_anchor=None,\n",
    "         dominant_baseline=None, font_weight=None, font_style=None, text_decoration=None, **kwargs):\n",
    "    \"A standard SVG `text` element\"\n",
    "    return ft_svg('text', *args, x=x, y=y, font_family=font_family, font_size=font_size, fill=fill,\n",
    "                 text_anchor=text_anchor, dominant_baseline=dominant_baseline, font_weight=font_weight,\n",
    "                 font_style=font_style, text_decoration=text_decoration, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><text x=\"10\" y=\"30\">Hello!</text></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Text(\"Hello!\", x=10, y=30))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Paths"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Paths in SVGs are more complex, so we add a small (optional) fluent interface for constructing them:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "class PathFT(FT):\n",
    "    def _append_cmd(self, cmd):\n",
    "        if not isinstance(getattr(self, 'd'), str): self.d = cmd\n",
    "        else: self.d += f' {cmd}'\n",
    "        return self\n",
    "    \n",
    "    def M(self, x, y):\n",
    "        \"Move to.\"\n",
    "        return self._append_cmd(f'M{x} {y}')\n",
    "\n",
    "    def L(self, x, y):\n",
    "        \"Line to.\"\n",
    "        return self._append_cmd(f'L{x} {y}')\n",
    "\n",
    "    def H(self, x):\n",
    "        \"Horizontal line to.\"\n",
    "        return self._append_cmd(f'H{x}')\n",
    "\n",
    "    def V(self, y):\n",
    "        \"Vertical line to.\"\n",
    "        return self._append_cmd(f'V{y}')\n",
    "\n",
    "    def Z(self):\n",
    "        \"Close path.\"\n",
    "        return self._append_cmd('Z')\n",
    "\n",
    "    def C(self, x1, y1, x2, y2, x, y):\n",
    "        \"Cubic Bézier curve.\"\n",
    "        return self._append_cmd(f'C{x1} {y1} {x2} {y2} {x} {y}')\n",
    "\n",
    "    def S(self, x2, y2, x, y):\n",
    "        \"Smooth cubic Bézier curve.\"\n",
    "        return self._append_cmd(f'S{x2} {y2} {x} {y}')\n",
    "\n",
    "    def Q(self, x1, y1, x, y):\n",
    "        \"Quadratic Bézier curve.\"\n",
    "        return self._append_cmd(f'Q{x1} {y1} {x} {y}')\n",
    "\n",
    "    def T(self, x, y):\n",
    "        \"Smooth quadratic Bézier curve.\"\n",
    "        return self._append_cmd(f'T{x} {y}')\n",
    "\n",
    "    def A(self, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y):\n",
    "        \"Elliptical Arc.\"\n",
    "        return self._append_cmd(f'A{rx} {ry} {x_axis_rotation} {large_arc_flag} {sweep_flag} {x} {y}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@delegates(ft_svg)\n",
    "def Path(d='', fill=None, stroke=None, stroke_width=None, **kwargs):\n",
    "    \"Create a standard `path` SVG element. This is a special object\"\n",
    "    return ft_svg('path', d=d, fill=fill, stroke=stroke, stroke_width=stroke_width, ft_cls=PathFT, **kwargs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's create a square shape, but using `Path` instead of `Rect`:\n",
    "\n",
    "- M(10, 10): Move to starting point (10, 10)\n",
    "- L(40, 10): Line to (40, 10) - top edge\n",
    "- L(40, 40): Line to (40, 40) - right edge\n",
    "- L(10, 40): Line to (10, 40) - bottom edge\n",
    "- Z(): Close path - connects back to start\n",
    "\n",
    "M = Move to, L = Line to, Z = Close path"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><path d=\" M10 10 L40 10 L40 40 L10 40 Z\" fill=\"none\" stroke=\"purple\" stroke-width=\"2\"></path></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Path(fill='none', stroke='purple', stroke_width=2\n",
    "         ).M(10, 10).L(40, 10).L(40, 40).L(10, 40).Z())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using curves we can create a spiral:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 100 50\" height=\"50\" width=\"100\"><path d=\" M25 25 C25 25 20 20 30 20 C40 20 40 30 30 30 C20 30 20 15 35 15 C50 15 50 35 25 35 C0 35 0 10 40 10 C80 10 80 40 25 40\" fill=\"none\" stroke=\"purple\" stroke-width=\"2\"></path></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "p = (Path(fill='none', stroke='purple', stroke_width=2)\n",
    "     .M(25, 25)\n",
    "     .C(25, 25, 20, 20, 30, 20)\n",
    "     .C(40, 20, 40, 30, 30, 30)\n",
    "     .C(20, 30, 20, 15, 35, 15)\n",
    "     .C(50, 15, 50, 35, 25, 35)\n",
    "     .C(0, 35, 0, 10, 40, 10)\n",
    "     .C(80, 10, 80, 40, 25, 40))\n",
    "demo(p, 50, 100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using arcs and curves we can create a map marker icon:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><path d=\" M25 45 C25 45 10 35 10 25 A15 15 0 1 1 40 25 C40 35 25 45 25 45 Z\" fill=\"red\"></path></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "p = (Path(fill='red')\n",
    "     .M(25,45)\n",
    "     .C(25,45,10,35,10,25)\n",
    "     .A(15,15,0,1,1,40,25)\n",
    "     .C(40,35,25,45,25,45)\n",
    "     .Z())\n",
    "demo(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Behind the scenes it's just creating regular SVG path `d` attr -- you can pass `d` in directly if you prefer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " M25 45 C25 45 10 35 10 25 A15 15 0 1 1 40 25 C40 35 25 45 25 45 Z\n"
     ]
    }
   ],
   "source": [
    "print(p.d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewbox=\"0 0 50 50\" height=\"50\" width=\"50\"><path d=\"M25 45 C25 45 10 35 10 25 A15 15 0 1 1 40 25 C40 35 25 45 25 45 Z\"></path></svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "demo(Path(d='M25 45 C25 45 10 35 10 25 A15 15 0 1 1 40 25 C40 35 25 45 25 45 Z'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L117){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.M\n",
       "\n",
       ">      PathFT.M (x, y)\n",
       "\n",
       "*Move to.*"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L117){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.M\n",
       "\n",
       ">      PathFT.M (x, y)\n",
       "\n",
       "*Move to.*"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(PathFT.M)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L121){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.L\n",
       "\n",
       ">      PathFT.L (x, y)\n",
       "\n",
       "*Line to.*"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L121){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.L\n",
       "\n",
       ">      PathFT.L (x, y)\n",
       "\n",
       "*Line to.*"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(PathFT.L)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L125){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.H\n",
       "\n",
       ">      PathFT.H (x)\n",
       "\n",
       "*Horizontal line to.*"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L125){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.H\n",
       "\n",
       ">      PathFT.H (x)\n",
       "\n",
       "*Horizontal line to.*"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(PathFT.H)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L129){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.V\n",
       "\n",
       ">      PathFT.V (y)\n",
       "\n",
       "*Vertical line to.*"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L129){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.V\n",
       "\n",
       ">      PathFT.V (y)\n",
       "\n",
       "*Vertical line to.*"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(PathFT.V)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L133){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.Z\n",
       "\n",
       ">      PathFT.Z ()\n",
       "\n",
       "*Close path.*"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L133){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.Z\n",
       "\n",
       ">      PathFT.Z ()\n",
       "\n",
       "*Close path.*"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(PathFT.Z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L137){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.C\n",
       "\n",
       ">      PathFT.C (x1, y1, x2, y2, x, y)\n",
       "\n",
       "*Cubic Bézier curve.*"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L137){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.C\n",
       "\n",
       ">      PathFT.C (x1, y1, x2, y2, x, y)\n",
       "\n",
       "*Cubic Bézier curve.*"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(PathFT.C)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L141){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.S\n",
       "\n",
       ">      PathFT.S (x2, y2, x, y)\n",
       "\n",
       "*Smooth cubic Bézier curve.*"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L141){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.S\n",
       "\n",
       ">      PathFT.S (x2, y2, x, y)\n",
       "\n",
       "*Smooth cubic Bézier curve.*"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(PathFT.S)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L145){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.Q\n",
       "\n",
       ">      PathFT.Q (x1, y1, x, y)\n",
       "\n",
       "*Quadratic Bézier curve.*"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L145){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.Q\n",
       "\n",
       ">      PathFT.Q (x1, y1, x, y)\n",
       "\n",
       "*Quadratic Bézier curve.*"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(PathFT.Q)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L149){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.T\n",
       "\n",
       ">      PathFT.T (x, y)\n",
       "\n",
       "*Smooth quadratic Bézier curve.*"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L149){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.T\n",
       "\n",
       ">      PathFT.T (x, y)\n",
       "\n",
       "*Smooth quadratic Bézier curve.*"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(PathFT.T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L153){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.A\n",
       "\n",
       ">      PathFT.A (rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y)\n",
       "\n",
       "*Elliptical Arc.*"
      ],
      "text/plain": [
       "---\n",
       "\n",
       "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/svg.py#L153){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n",
       "\n",
       "### PathFT.A\n",
       "\n",
       ">      PathFT.A (rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y)\n",
       "\n",
       "*Elliptical Arc.*"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_doc(PathFT.A)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HTMX helpers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "svg_inb = dict(hx_select=\"svg>*\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def SvgOob(*args, **kwargs):\n",
    "    \"Wraps an SVG shape as required for an HTMX OOB swap\"\n",
    "    return Template(Svg(*args, **kwargs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When returning an SVG shape out-of-band (OOB) in HTMX, you need to wrap it with `SvgOob` to have it appear correctly. (`SvgOob` is just a shortcut for `Template(Svg(...))`, which is the trick that makes SVG OOB swaps work.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def SvgInb(*args, **kwargs):\n",
    "    \"Wraps an SVG shape as required for an HTMX inband swap\"\n",
    "    return Svg(*args, **kwargs), HtmxResponseHeaders(hx_reselect='svg>*')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When returning an SVG shape in-band in HTMX, either have the calling element include `hx_select='svg>*'`, or `**svg_inb` (which are two ways of saying the same thing), or wrap the response with `SvgInb` to have it appear correctly. (`SvgInb` is just a shortcut for the tuple `(Svg(...), HtmxResponseHeaders(hx_reselect='svg>*'))`, which is the trick that makes SVG in-band swaps work.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Export -"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| hide\n",
    "import nbdev; nbdev.nbdev_export()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
